package com.vcc.core.util;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.vcc.core.exception.ProgramException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.val;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Random;

public class CodeUtil {

    private static final String SOURCE_CODES = "123456789ABCDEFGHIJKLMNOBQRSTUVWXYZ";

    //    private const val SOURCE_CODES = "1234567890"
    private static final Integer height = 40;
    private static final Integer width = 95;

    //图片
    private static final Integer QR_COLOR = 0xFF000000; // 默认是黑色
    private static final Integer BG_WHITE = 0xFFFFFFFF; // 背景颜色
    private static final Integer QR_CODE_SIZE = 200;

    public static void generate(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession();
        String code = createCodeStr(4);
        int verifySize = code.length();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Random rand = new Random();
        Graphics2D g2 = image.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        ArrayList<Color> colors = new ArrayList<Color>(5);
        ArrayList<Color> colorSpaces = new ArrayList<Color>() {
            {
                add(Color.WHITE);
                add(Color.CYAN);
                add(Color.GRAY);
                add(Color.LIGHT_GRAY);
                add(Color.MAGENTA);
                add(Color.ORANGE);
                add(Color.PINK);
                add(Color.YELLOW);
            }
        };
        Float[] fractions = new Float[5];
        for (int i = 0; i < 5; i++) {
            colors.set(i, colorSpaces.get(rand.nextInt(colorSpaces.size())));
            fractions[i] = rand.nextFloat();
        }
        Arrays.sort(fractions);
        // 设置边框色
        g2.setColor(Color.GRAY);
        g2.fillRect(0, 0, width, height);
        Color c = getRandColor(200, 250);
        // 设置背景色
        g2.setColor(c);
        g2.fillRect(0, 2, width, height - 4);
        //绘制干扰线
        Random random = new Random();
        // 设置线条的颜色
        g2.setColor(getRandColor(160, 200));
        for (int i = 0; i < 20; i++) {
            int x = random.nextInt(width - 1);
            int y = random.nextInt(height - 1);
            int xl = random.nextInt(6) + 1;
            int yl = random.nextInt(12) + 1;
            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
        }
        // 添加噪点
        // 噪声率
        float yawpRate = 0.05f;
        int area = (int) (yawpRate * width * height);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int rgb = getRandomIntColor();
            image.setRGB(x, y, rgb);
        }
        g2.setColor(getRandColor(100, 160));
        int fontSize = height - 4;
        Font font = new Font("Algerian", Font.ITALIC, fontSize);
        g2.setFont(font);
        char[] chars = code.toCharArray();
        for (int i = 0; i < verifySize; i++) {
            AffineTransform affine = new AffineTransform();
            affine.setToRotation(
                    Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
                    width / verifySize * i + fontSize / (double) 2,
                    height / (double) 2
            );
            g2.setTransform(affine);
            g2.drawChars(chars, i, 1, (width - 10) / verifySize * i + 5, height / 2 + fontSize / 2 - 10);
        }
        g2.dispose();
        OutputStream os = null;
        try {
            session.setAttribute("p_code", code);
            os = response.getOutputStream();
            ImageIO.write(image, "jpg", os);
            os.flush();
            os.close();
        } catch (Exception e) {
            throw new ProgramException("生成验证码失败");
        }
    }


    /**
     * 生成包含字符串信息的二维码图片
     *
     * @param content 二维码携带信息
     * @throws WriterException
     * @throws IOException
     */
    public static void createQrCode(String content, HttpServletResponse response) {
        try {
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
            BitMatrix byteMatrix =
                    multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, QR_CODE_SIZE, QR_CODE_SIZE, getHintMap());
            BufferedImage image = new BufferedImage(QR_CODE_SIZE, QR_CODE_SIZE, BufferedImage.TYPE_INT_RGB);
            for (int i = 0; i < QR_CODE_SIZE; i++) {
                for (int j = 0; j < QR_CODE_SIZE; j++) {
                    image.setRGB(i, j, byteMatrix.get(i, j) ? (int) 0xFF000000 : (int) 0xFFFFFFFF);
                }
            }
            OutputStream os = response.getOutputStream();
            ImageIO.write(image, "jpg", os);
            os.flush();
            os.close();
        } catch (Exception e) {
            throw new ProgramException("二维码生成错误");
        }
    }

    public static String createQrCode(String content) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        try {
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
            BitMatrix byteMatrix =
                    multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, QR_CODE_SIZE, QR_CODE_SIZE, getHintMap());
            BufferedImage image = new BufferedImage(QR_CODE_SIZE, QR_CODE_SIZE, BufferedImage.TYPE_INT_RGB);
            for (int i = 0; i < QR_CODE_SIZE; i++) {
                for (int j = 0; j < QR_CODE_SIZE; j++) {
                    image.setRGB(i, j, byteMatrix.get(i, j) ? (int) 0xFF000000 : (int) 0xFFFFFFFF);
                }
            }
            ImageIO.write(image, "png", stream);
//            String base64 = new BASE64Encoder().encode(stream.toByteArray());
            stream.flush();
            stream.close();
            return "data:image/png;base64," + Base64Util.encodeToString(stream.toByteArray());
        } catch (Exception e) {
            throw new ProgramException("二维码生成错误");
        }
    }

    /**
     * 生成包含字符串信息的二维码图片
     *
     * @param content 二维码携带信息
     * @throws WriterException
     * @throws IOException
     */
    public static void createQrCode(String content, String path) {
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        try {
            BitMatrix byteMatrix =
                    multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, QR_CODE_SIZE, QR_CODE_SIZE, getHintMap());

            Path filePath = FileSystems.getDefault().getPath(path);
            MatrixToImageWriter.writeToPath(byteMatrix, "PNG", filePath);
        } catch (Exception e) {
            throw new ProgramException("生成文件失败");
        }

    }

    public static Result analysisQrCode(InputStream inputStream) {
        try {
            //从输入流中获取字符串信息
            BufferedImage image = ImageIO.read(inputStream);
            //将图像转换为二进制位图源
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            val reader = new QRCodeReader();
            Result result = null;
            try {
                result = reader.decode(bitmap);
            } catch (ReaderException e) {
                throw new ProgramException("生成失败");
            }
            return result;
        } catch (Exception e) {
            throw new ProgramException("二维码解析错误");
        }
    }


    private static Hashtable<EncodeHintType, Object> getHintMap() {
        //设置二维码纠错级别ＭＡＰ
        Hashtable<EncodeHintType, Object> hintMap = new Hashtable<EncodeHintType, Object>();
        hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 矫错级别
        hintMap.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 矫错级别
        hintMap.put(EncodeHintType.MARGIN, 0); // 矫错级别
        return hintMap;
    }

    private static final String SOURCE_NAME = "ABCDEFGHIJKLMNOBQRSTUVWXYZ";

    public static String createNameStr(int codeLength) {
        Random random = new Random();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < codeLength; i++) {
            val key = random.nextInt(SOURCE_NAME.length() - 1);
            builder.append(SOURCE_NAME.charAt(key));
        }
        return builder.toString();
    }

    public static String createCodeStr(int codeLength) {
        Random random = new Random();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < codeLength; i++) {
            val key = random.nextInt(SOURCE_CODES.length() - 1);
            builder.append(SOURCE_CODES.charAt(key));
        }
        return builder.toString();
    }

    private static int[] getRandomRgb() {
        int[] rgb = new int[3];
        for (int i = 0; i < 3; i++) {
            rgb[i] = new Random().nextInt(255);

        }
        return rgb;
    }

    private static int getRandomIntColor() {
        int[] rgb = getRandomRgb();
        int color = 0;
        for (int c : rgb) {
            color <<= 8;
            color |= c;
        }
        return color;
    }

    private static final Color getRandColor(int fc, int bc) {
        Random random = new Random();
        if (fc > 255) {
            fc = 255;
        }

        if (bc > 255) {
            bc = 255;
        }

        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    public static String getSmsCode() {
        Random random = new Random();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 5; i++) {
            int key = random.nextInt(9);
            stringBuilder.append(key);
        }
        if (stringBuilder.toString().startsWith("0")) {
            return getSmsCode();
        } else {
            return stringBuilder.toString();
        }
    }

}
